The world cup 2022 will be hosted by Qatar in 2022. We wanted to try and predict which country has the biggest probability of winning the tournament by using supervised learning.

1. Data

The data that we will be using is already cleaned and does not require pre-processing.

The data set provides a complete overview of all international football matches played since the 90s. On top of that, the strength of each team is provided by incorporating the FIFA rankings as well as player strengths based on the EA Sport FIFA video game. It is available on kaggle

Variables

  • date : date of the match
  • home_team : name of the home team
  • away_team : name of the away team
  • home_team_continent : continent of the home team
  • away_team_continent : continent of the away team
  • home_team_fifa_rank : FIFA rank of the home team when the match took place
  • away_team_fifa_rank : FIFA rank of the away team when the match took place
  • home_team_total_fifa_points : total number of FIFA points of the home team at the time of the match
  • away_team_total_fifa_points : total number of FIFA points of the away team at the time of the match
  • home_team_score : full-time home score (excluding penalty shootout)
  • away_team_score : full-time away score (excluding penalty shootout)
  • tournament : name of tournament
  • city : name of the city where the match was played
  • country : name of the country where the match was played
  • neutral_location :
    • TRUE : the match was played at a neutral venue
  • shoot_out:
    • TRUE : the match included a penalty shootout
  • home_team_result : result of the home team (including penalty shootout)
  • home_team_goalkeeper_score : FIFA game score of the highest ranked GK of the home team
  • away_team_goalkeeper_score : FIFA game score of the highest ranked GK of the away team
  • home_team_mean_defense_score : Average FIFA game score of the 4 highest ranked defensive players of the home team
  • away_team_mean_defense_score : Average FIFA game score of the 4 highest ranked defensive players of the away team
  • home_team_mean_midfield_score : Average FIFA game score of the 4 highest ranked midfield players of the home team
  • away_team_mean_midfield_score : Average FIFA game score of the 4 highest ranked midfield players of the away team
  • home_team_mean_offense_score : Average FIFA game score of the 3 highest ranked attacking players of the home team, including wing players
  • away_team_mean_offense_score : Average FIFA game score of the 3 highest ranked attacking players of the away team, including wing players

2. EDA

# class of each variable
spec(input_data)
cols(
  date = col_date(format = ""),
  home_team = col_character(),
  away_team = col_character(),
  home_team_continent = col_character(),
  away_team_continent = col_character(),
  home_team_fifa_rank = col_double(),
  away_team_fifa_rank = col_double(),
  home_team_total_fifa_points = col_double(),
  away_team_total_fifa_points = col_double(),
  home_team_score = col_double(),
  away_team_score = col_double(),
  tournament = col_character(),
  city = col_character(),
  country = col_character(),
  neutral_location = col_logical(),
  shoot_out = col_character(),
  home_team_result = col_character(),
  home_team_goalkeeper_score = col_double(),
  away_team_goalkeeper_score = col_double(),
  home_team_mean_defense_score = col_double(),
  home_team_mean_offense_score = col_double(),
  home_team_mean_midfield_score = col_double(),
  away_team_mean_defense_score = col_double(),
  away_team_mean_offense_score = col_double(),
  away_team_mean_midfield_score = col_double()
)
# summary
skim_without_charts(input_data)
── Data Summary ────────────────────────
                           Values    
Name                       input_data
Number of rows             23921     
Number of columns          25        
_______________________              
Column type frequency:               
  character                9         
  Date                     1         
  logical                  1         
  numeric                  14        
________________________             
Group variables            None      

Missing data

input_data %>%
  summarise_all(list(~is.na(.)))%>%
  pivot_longer(everything(),
               names_to = "variables", values_to="missing") %>%
  count(variables, missing) %>%
  ggplot(aes(y=variables,x=n,fill=missing))+
  geom_col()+
  scale_fill_manual(values=c("#A3BE8C","#EBCB8B"))+
  theme(axis.title.y=element_blank())

Top 10 teams in 2022

# Get the ranking of all home teams
home <-
  input_data %>% 
  select(date, home_team, home_team_fifa_rank) %>% 
  rename(team = home_team, ranking = home_team_fifa_rank)

# Get the ranking of all away teams
away <-
  input_data %>% 
  select(date, away_team, away_team_fifa_rank) %>% 
  rename(team = away_team, ranking = away_team_fifa_rank)

# Combine both data frames into one
fifa_ranking <- rbind(home, away)

# Get the latest ranking of each country based on their most recent match
latest_fifa_ranking <-
  fifa_ranking %>% 
  arrange(team, desc(date)) %>% 
  group_by(team) %>% 
  mutate(row_number = row_number(team)) %>% 
  filter(row_number == 1) %>% 
  select(-row_number, -date) %>% 
  arrange(ranking)
  
head(latest_fifa_ranking, 10)

FIFA rankings over time

top5_list <- head(latest_fifa_ranking, 5)$team

top5_ranking <-
  fifa_ranking  %>% 
  filter(team %in% top5_list)

p <-
  ggplot(data = top5_ranking,
         mapping = aes(
           x = date,
           y = ranking,
           group = team,
           color = team
         )) +
  geom_line() +
  scale_y_reverse() +
  labs(
    x = "Date",
    y = "FIFA Ranking",
    color = "Team",
    title = "FIFA Rankings of the 2022 Top 5 teams"
  )

ggplotly(p)
NA

Teams with strongest GK

# Gather goalkeeper data from matches
gk_home <-
  input_data %>% 
  select(date, home_team, home_team_goalkeeper_score) %>% 
  rename(team = home_team, goalkeeper_rating = home_team_goalkeeper_score)

gk_away <-
  input_data %>% 
  select(date, away_team, away_team_goalkeeper_score) %>% 
  rename(team = away_team, goalkeeper_rating = away_team_goalkeeper_score)

gk_rating <- drop_na(rbind(gk_home, gk_away))

# Get latest rating of each team's goalkeeper and show top 10
latest_gk_rating <-
  gk_rating %>% 
  arrange(team, desc(date)) %>% 
  group_by(team) %>% 
  mutate(row_number = row_number(team)) %>% 
  filter(row_number == 1) %>% 
  select(-row_number, -date) %>% 
  arrange(-goalkeeper_rating)

ggplot(data = head(latest_gk_rating, 10), mapping = aes(x=goalkeeper_rating, y=reorder(team, goalkeeper_rating), label=goalkeeper_rating)) +
  geom_col(fill="#88C0D0") +
  geom_text(position = position_stack(vjust = 0.5)) +
  labs(title = "Top 10 teams with the strongest goalkeeper",
       subtitle = "Based on the highest rated goalkeeper of each team",
       x="Goalkeeper Rating",
       y="Country")

Teams with strongest defense

# Gather goalkeeper and defense data from matches
def_home <-
  input_data %>% 
  select(date, home_team, home_team_goalkeeper_score, home_team_mean_defense_score) %>% 
  rename(team = home_team, goalkeeper_rating = home_team_goalkeeper_score, mean_defense_rating = home_team_mean_defense_score)

def_away <-
  input_data %>% 
  select(date, away_team, away_team_goalkeeper_score, away_team_mean_defense_score) %>% 
  rename(team = away_team, goalkeeper_rating = away_team_goalkeeper_score, mean_defense_rating = away_team_mean_defense_score)

def_rating <- drop_na(rbind(def_home, def_away))

# Get latest combined rating of each team and show top 10
latest_def_rating <-
  def_rating %>% 
  arrange(team, desc(date)) %>% 
  mutate(total_def = goalkeeper_rating + mean_defense_rating) %>% 
  group_by(team) %>% 
  mutate(row_number = row_number(team)) %>% 
  filter(row_number==1) %>% 
  arrange(-total_def) %>% 
  select(-row_number, -date)

ggplot(data = head(latest_def_rating, 10), mapping=aes(x=total_def, y=reorder(team, total_def), label=total_def)) + 
  geom_col(fill="#88C0D0") +
  geom_text(position = position_stack(vjust = 0.5)) +
  labs(title = "Top 10 teams with the strongest defense",
       subtitle = "Based on goalkeeper and mean defense ratings",
       x = "Total Defense Rating",
       y = "Teams") 

Teams with strongest midfield

mid_home <-
  input_data %>% 
  select(date, home_team, home_team_mean_midfield_score) %>% 
  rename(team = home_team, midfield_rating = home_team_mean_midfield_score)

mid_away <-
  input_data %>% 
  select(date, away_team, away_team_mean_midfield_score) %>% 
  rename(team = away_team, midfield_rating = away_team_mean_midfield_score)

mid_rating <- drop_na(rbind(mid_home, mid_away))

# Get latest midfield rating of each team and show top 10
latest_mid_rating <-
  mid_rating %>% 
  arrange(team, desc(date)) %>% 
  group_by(team) %>% 
  mutate(row_number = row_number(team)) %>% 
  filter(row_number == 1) %>% 
  arrange(-midfield_rating) %>% 
  select(-date, -row_number)

ggplot(data = head(latest_mid_rating, 10), mapping=aes(x=midfield_rating, y=reorder(team, midfield_rating), label=midfield_rating)) + 
  geom_col(fill= "#88C0D0") +
  geom_text(position = position_stack(vjust = 0.5)) +
  labs(title = "Top 10 teams with the strongest midfield",
       subtitle = "Based on the average rating of the 4 highest rated midfield players of each team",
       x = "Midfield Rating",
       y = "Teams")

Teams with strongest offense

off_home <-
  input_data %>% 
  select(date, home_team, home_team_mean_offense_score) %>% 
  rename(team = home_team, offense_rating = home_team_mean_offense_score)

off_away <-
  input_data %>% 
  select(date, away_team, away_team_mean_offense_score) %>% 
  rename(team = away_team, offense_rating = away_team_mean_offense_score)

off_rating <- drop_na(rbind(off_home, off_away))

# Get latest offense rating of each team and show top 10
latest_off_rating <-
  off_rating %>% 
  arrange(team, desc(date)) %>% 
  group_by(team) %>% 
  mutate(row_number = row_number(team)) %>% 
  filter(row_number == 1) %>% 
  arrange(-offense_rating) %>% 
  select(-date, -row_number)

ggplot(data = head(latest_off_rating, 10), mapping=aes(x=offense_rating, y=reorder(team, offense_rating), label=offense_rating)) +
  geom_col(fill="#88C0D0") +
  geom_text(position = position_stack(vjust = 0.5)) +
  labs(title="Top 10 teams with the strongest offense",
      subtitle="Based on the average rating of the 3 highest rated offensive players of each team",
      x="Offense Rating",
      y="Teams")

Is it better to play at home ?

home_team_advantage <-
  input_data %>% 
  filter(neutral_location == FALSE) %>% 
  count(home_team_result) %>% 
  mutate(percentage = label_percent()(n/sum(n)))

ggplot(data = home_team_advantage, mapping=aes(x="", y=n, fill=home_team_result)) +
  geom_bar(width = 1, stat = "identity", color="white") +
  coord_polar("y") +
  scale_fill_manual(values = c("#EBCB8B", "#BF616A",
                               "#A3BE8C")) +
  theme_void() +
  labs(title = "Distribution of match results of home teams",
       subtitle = "Excluding matches played at neutral locations",
       fill="Result")

Correlation Matrix

# select numeric columns only
input_numeric_data <- input_data %>%
  select_if(is.numeric) %>%
  drop_na()

# rename variables for easier correlation plot visualization
input_numeric_data <- input_numeric_data %>% rename(
  rank1 = home_team_fifa_rank,
  rank2 = away_team_fifa_rank,
  total_fifa_points1 = home_team_total_fifa_points,
  total_fifa_points2 = away_team_total_fifa_points,
  score1 = home_team_score,
  score2 = away_team_score,
  gk_score1 = home_team_goalkeeper_score,
  gk_score2 = away_team_goalkeeper_score,
  df_score1 = home_team_mean_defense_score,
  df_score2 = away_team_mean_defense_score,
  att_score1 = home_team_mean_offense_score,
  att_score2 = away_team_mean_offense_score,
  mf_score1 = home_team_mean_midfield_score,
  mf_score2 = away_team_mean_midfield_score
)

# create correlation plot
input_numeric_data %>%
  cor() %>%
  corrplot(
    type = "upper",
    diag = FALSE,
    col=colorRampPalette(c("firebrick","lightyellow","green4"))(100),
    method = "shade",
    shade.col = NA,
    tl.col = "black",
    tl.srt = 45
  )

2. Data Processing / Feature Engineering

Create new features

output_data$win <- output_data$score_difference > 0
Warning: Unknown or uninitialised column: `score_difference`.
Error:
! Assigned data `output_data$score_difference > 0` must be compatible with existing data.
✖ Existing data has 23921 rows.
✖ Assigned data has 0 rows.
ℹ Only vectors of size 1 are recycled.
Backtrace:
  1. base::`$<-`(`*tmp*`, win, value = `<lgl>`)
 12. tibble (local) `<fn>`(`<vctrs___>`)

Model

# create training and test set

sample <- sample(c(TRUE, FALSE), nrow(output_data), replace=TRUE, prob=c(0.7,0.3))
train <- output_data[sample, ]
test <- output_data[!sample, ]

# fit logistic regression model
logreg <- glm(win ~ average_rank + rank_diff + point_diff, family = "binomial", data = train)
summary(logreg)

Call:
glm(formula = win ~ average_rank + rank_diff + point_diff, family = "binomial", 
    data = train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.6642  -1.0028  -0.3857   1.0260   2.5995  

Coefficients:
               Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -0.2726775  0.0330011  -8.263  < 2e-16 ***
average_rank  0.0018669  0.0003617   5.161 2.46e-07 ***
rank_diff    -0.0196047  0.0004886 -40.120  < 2e-16 ***
point_diff    0.0003186  0.0001322   2.410    0.016 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 23081  on 16657  degrees of freedom
Residual deviance: 19729  on 16654  degrees of freedom
AIC: 19737

Number of Fisher Scoring iterations: 4
# calc probability of win for each team in test dataset
predicted <- predict(logreg, test, type="response")


# convert wins to 1 and 0
test$win <- ifelse(test$win==TRUE, 1, 0)

# find optimal cutoff probability to use to maximize accuracy
optimal <- optimalCutoff(test$win, predicted)[1]
optimal
[1] 0.5171179
# confusion matrix
confusionMatrix(test$win, predicted)

# calculate miss classification error rate
misClassError(test$win, predicted, threshold = optimal)
[1] 0.3192
# ROC
plotROC(test$win, predicted)

Test

index(wc_rankings_away)
Error in index(wc_rankings_away) : could not find function "index"
    row <- data.frame(matrix(nrow = 0, ncol = length(colnames(test)))) %>%
      colnames(row) <- colnames(test)
Error in data.frame(matrix(nrow = 0, ncol = length(colnames(test)))) %>%  : 
  target of assignment expands to non-language object
LS0tCnRpdGxlOiAiV29ybGQgQ3VwIDIwMjIiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2tpbXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KEluZm9ybWF0aW9uVmFsdWUpCmBgYAoKVGhlIHdvcmxkIGN1cCAyMDIyIHdpbGwgYmUgaG9zdGVkIGJ5IFFhdGFyIGluIDIwMjIuIFdlIHdhbnRlZCB0byB0cnkgYW5kIHByZWRpY3QKd2hpY2ggY291bnRyeSBoYXMgdGhlIGJpZ2dlc3QgcHJvYmFiaWxpdHkgb2Ygd2lubmluZyB0aGUgdG91cm5hbWVudCBieSB1c2luZyBzdXBlcnZpc2VkIGxlYXJuaW5nLgoKIyAxLiBEYXRhCgpUaGUgZGF0YSB0aGF0IHdlIHdpbGwgYmUgdXNpbmcgaXMgYWxyZWFkeSBjbGVhbmVkIGFuZCBkb2VzIG5vdCByZXF1aXJlIHByZS1wcm9jZXNzaW5nLgoKVGhlIGRhdGEgc2V0IHByb3ZpZGVzIGEgY29tcGxldGUgb3ZlcnZpZXcgb2YgYWxsIGludGVybmF0aW9uYWwgZm9vdGJhbGwgbWF0Y2hlcyBwbGF5ZWQgc2luY2UgdGhlIDkwcy4gT24gdG9wIG9mIHRoYXQsIHRoZSBzdHJlbmd0aCBvZiBlYWNoIHRlYW0gaXMgcHJvdmlkZWQgYnkgaW5jb3Jwb3JhdGluZyB0aGUgRklGQSByYW5raW5ncyBhcyB3ZWxsIGFzIHBsYXllciBzdHJlbmd0aHMgYmFzZWQgb24gdGhlIEVBIFNwb3J0IEZJRkEgdmlkZW8gZ2FtZS4gSXQgaXMgYXZhaWxhYmxlIG9uIFtrYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvYnJlbmRhODkvZmlmYS13b3JsZC1jdXAtMjAyMikKCiMjIFZhcmlhYmxlcwoKLSBgZGF0ZWAgOiBkYXRlIG9mIHRoZSBtYXRjaAotIGBob21lX3RlYW1gIDogbmFtZSBvZiB0aGUgaG9tZSB0ZWFtCi0gYGF3YXlfdGVhbWAgOiBuYW1lIG9mIHRoZSBhd2F5IHRlYW0KLSBgaG9tZV90ZWFtX2NvbnRpbmVudGAgOiBjb250aW5lbnQgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fY29udGluZW50YCA6IGNvbnRpbmVudCBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9maWZhX3JhbmtgIDogRklGQSByYW5rIG9mIHRoZSBob21lIHRlYW0gd2hlbiB0aGUgbWF0Y2ggdG9vayBwbGFjZQotIGBhd2F5X3RlYW1fZmlmYV9yYW5rYCA6IEZJRkEgcmFuayBvZiB0aGUgYXdheSB0ZWFtIHdoZW4gdGhlIG1hdGNoIHRvb2sgcGxhY2UKLSBgaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzYCA6IHRvdGFsIG51bWJlciBvZiBGSUZBIHBvaW50cyBvZiB0aGUgaG9tZSB0ZWFtIGF0IHRoZSB0aW1lIG9mIHRoZSBtYXRjaAotIGBhd2F5X3RlYW1fdG90YWxfZmlmYV9wb2ludHNgIDogdG90YWwgbnVtYmVyIG9mIEZJRkEgcG9pbnRzIG9mIHRoZSBhd2F5IHRlYW0gYXQgdGhlIHRpbWUgb2YgdGhlIG1hdGNoCi0gYGhvbWVfdGVhbV9zY29yZWAgOiBmdWxsLXRpbWUgaG9tZSBzY29yZSAoZXhjbHVkaW5nIHBlbmFsdHkgc2hvb3RvdXQpCi0gYGF3YXlfdGVhbV9zY29yZWAgOiBmdWxsLXRpbWUgYXdheSBzY29yZSAoZXhjbHVkaW5nIHBlbmFsdHkgc2hvb3RvdXQpCi0gYHRvdXJuYW1lbnRgIDogbmFtZSBvZiB0b3VybmFtZW50Ci0gYGNpdHlgIDogbmFtZSBvZiB0aGUgY2l0eSB3aGVyZSB0aGUgbWF0Y2ggd2FzIHBsYXllZAotIGBjb3VudHJ5YCA6IG5hbWUgb2YgdGhlIGNvdW50cnkgd2hlcmUgdGhlIG1hdGNoIHdhcyBwbGF5ZWQKLSBgbmV1dHJhbF9sb2NhdGlvbmAgOgogIC0gYFRSVUVgIDogdGhlIG1hdGNoIHdhcyBwbGF5ZWQgYXQgYSBuZXV0cmFsIHZlbnVlCi0gYHNob290X291dGA6CiAgLSBgVFJVRWAgOiB0aGUgbWF0Y2ggaW5jbHVkZWQgYSBwZW5hbHR5IHNob290b3V0Ci0gYGhvbWVfdGVhbV9yZXN1bHRgIDogcmVzdWx0IG9mIHRoZSBob21lIHRlYW0gKGluY2x1ZGluZyBwZW5hbHR5IHNob290b3V0KQotIGBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZWAgOiBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIGhpZ2hlc3QgcmFua2VkIEdLIG9mIHRoZSBob21lIHRlYW0KLSBgYXdheV90ZWFtX2dvYWxrZWVwZXJfc2NvcmVgIDogRklGQSBnYW1lIHNjb3JlIG9mIHRoZSBoaWdoZXN0IHJhbmtlZCBHSyBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmVgIDogQXZlcmFnZSBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIDQgaGlnaGVzdCByYW5rZWQgZGVmZW5zaXZlIHBsYXllcnMgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlYCA6IEF2ZXJhZ2UgRklGQSBnYW1lIHNjb3JlIG9mIHRoZSA0IGhpZ2hlc3QgcmFua2VkIApkZWZlbnNpdmUgcGxheWVycyBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlYCA6IEF2ZXJhZ2UgRklGQSBnYW1lIHNjb3JlIG9mIHRoZSA0IGhpZ2hlc3QgcmFua2VkIG1pZGZpZWxkIHBsYXllcnMgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZWAgOiBBdmVyYWdlIEZJRkEgZ2FtZSBzY29yZSBvZiB0aGUgNCBoaWdoZXN0IHJhbmtlZCBtaWRmaWVsZCBwbGF5ZXJzIG9mIHRoZSBhd2F5IHRlYW0KLSBgaG9tZV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZWAgOiBBdmVyYWdlIEZJRkEgZ2FtZSBzY29yZSBvZiB0aGUgMyBoaWdoZXN0IHJhbmtlZCBhdHRhY2tpbmcgcGxheWVycyBvZiB0aGUgaG9tZSB0ZWFtLCBpbmNsdWRpbmcgd2luZyBwbGF5ZXJzCi0gYGF3YXlfdGVhbV9tZWFuX29mZmVuc2Vfc2NvcmVgIDogQXZlcmFnZSBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIDMgaGlnaGVzdCByYW5rZWQgYXR0YWNraW5nIHBsYXllcnMgb2YgdGhlIGF3YXkgdGVhbSwgaW5jbHVkaW5nIHdpbmcgcGxheWVycwoKIyAyLiBFREEKCmBgYHtyfQojIGNsYXNzIG9mIGVhY2ggdmFyaWFibGUKc3BlYyhpbnB1dF9kYXRhKQpgYGAKYGBge3J9CiMgc3VtbWFyeQpza2ltX3dpdGhvdXRfY2hhcnRzKGlucHV0X2RhdGEpCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpgYGB7cn0KaW5wdXRfZGF0YSAlPiUKICBzdW1tYXJpc2VfYWxsKGxpc3QofmlzLm5hKC4pKSklPiUKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZXMiLCB2YWx1ZXNfdG89Im1pc3NpbmciKSAlPiUKICBjb3VudCh2YXJpYWJsZXMsIG1pc3NpbmcpICU+JQogIGdncGxvdChhZXMoeT12YXJpYWJsZXMseD1uLGZpbGw9bWlzc2luZykpKwogIGdlb21fY29sKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM0JFOEMiLCIjRUJDQjhCIikpKwogIHRoZW1lKGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpCmBgYAoKCiMjIFRvcCAxMCB0ZWFtcyBpbiAyMDIyCgpgYGB7cn0KIyBHZXQgdGhlIHJhbmtpbmcgb2YgYWxsIGhvbWUgdGVhbXMKaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9maWZhX3JhbmspICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgcmFua2luZyA9IGhvbWVfdGVhbV9maWZhX3JhbmspCgojIEdldCB0aGUgcmFua2luZyBvZiBhbGwgYXdheSB0ZWFtcwphd2F5IDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGF3YXlfdGVhbSwgYXdheV90ZWFtX2ZpZmFfcmFuaykgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCByYW5raW5nID0gYXdheV90ZWFtX2ZpZmFfcmFuaykKCiMgQ29tYmluZSBib3RoIGRhdGEgZnJhbWVzIGludG8gb25lCmZpZmFfcmFua2luZyA8LSByYmluZChob21lLCBhd2F5KQoKIyBHZXQgdGhlIGxhdGVzdCByYW5raW5nIG9mIGVhY2ggY291bnRyeSBiYXNlZCBvbiB0aGVpciBtb3N0IHJlY2VudCBtYXRjaApsYXRlc3RfZmlmYV9yYW5raW5nIDwtCiAgZmlmYV9yYW5raW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBncm91cF9ieSh0ZWFtKSAlPiUgCiAgbXV0YXRlKHJvd19udW1iZXIgPSByb3dfbnVtYmVyKHRlYW0pKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIgPT0gMSkgJT4lIAogIHNlbGVjdCgtcm93X251bWJlciwgLWRhdGUpICU+JSAKICBhcnJhbmdlKHJhbmtpbmcpCiAgCmhlYWQobGF0ZXN0X2ZpZmFfcmFua2luZywgMTApCmBgYAoKIyMgRklGQSByYW5raW5ncyBvdmVyIHRpbWUKCmBgYHtyfQp0b3A1X2xpc3QgPC0gaGVhZChsYXRlc3RfZmlmYV9yYW5raW5nLCA1KSR0ZWFtCgp0b3A1X3JhbmtpbmcgPC0KICBmaWZhX3JhbmtpbmcgICU+JSAKICBmaWx0ZXIodGVhbSAlaW4lIHRvcDVfbGlzdCkKCnAgPC0KICBnZ3Bsb3QoZGF0YSA9IHRvcDVfcmFua2luZywKICAgICAgICAgbWFwcGluZyA9IGFlcygKICAgICAgICAgICB4ID0gZGF0ZSwKICAgICAgICAgICB5ID0gcmFua2luZywKICAgICAgICAgICBncm91cCA9IHRlYW0sCiAgICAgICAgICAgY29sb3IgPSB0ZWFtCiAgICAgICAgICkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGxhYnMoCiAgICB4ID0gIkRhdGUiLAogICAgeSA9ICJGSUZBIFJhbmtpbmciLAogICAgY29sb3IgPSAiVGVhbSIsCiAgICB0aXRsZSA9ICJGSUZBIFJhbmtpbmdzIG9mIHRoZSAyMDIyIFRvcCA1IHRlYW1zIgogICkKCmdncGxvdGx5KHApCgpgYGAKCiMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IEdLCgpgYGB7cn0KIyBHYXRoZXIgZ29hbGtlZXBlciBkYXRhIGZyb20gbWF0Y2hlcwpna19ob21lIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGhvbWVfdGVhbSwgaG9tZV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcgPSBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZSkKCmdrX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fZ29hbGtlZXBlcl9zY29yZSkgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCBnb2Fsa2VlcGVyX3JhdGluZyA9IGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlKQoKZ2tfcmF0aW5nIDwtIGRyb3BfbmEocmJpbmQoZ2tfaG9tZSwgZ2tfYXdheSkpCgojIEdldCBsYXRlc3QgcmF0aW5nIG9mIGVhY2ggdGVhbSdzIGdvYWxrZWVwZXIgYW5kIHNob3cgdG9wIDEwCmxhdGVzdF9na19yYXRpbmcgPC0KICBna19yYXRpbmcgJT4lIAogIGFycmFuZ2UodGVhbSwgZGVzYyhkYXRlKSkgJT4lIAogIGdyb3VwX2J5KHRlYW0pICU+JSAKICBtdXRhdGUocm93X251bWJlciA9IHJvd19udW1iZXIodGVhbSkpICU+JSAKICBmaWx0ZXIocm93X251bWJlciA9PSAxKSAlPiUgCiAgc2VsZWN0KC1yb3dfbnVtYmVyLCAtZGF0ZSkgJT4lIAogIGFycmFuZ2UoLWdvYWxrZWVwZXJfcmF0aW5nKQoKZ2dwbG90KGRhdGEgPSBoZWFkKGxhdGVzdF9na19yYXRpbmcsIDEwKSwgbWFwcGluZyA9IGFlcyh4PWdvYWxrZWVwZXJfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcpLCBsYWJlbD1nb2Fsa2VlcGVyX3JhdGluZykpICsKICBnZW9tX2NvbChmaWxsPSIjODhDMEQwIikgKwogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAiVG9wIDEwIHRlYW1zIHdpdGggdGhlIHN0cm9uZ2VzdCBnb2Fsa2VlcGVyIiwKICAgICAgIHN1YnRpdGxlID0gIkJhc2VkIG9uIHRoZSBoaWdoZXN0IHJhdGVkIGdvYWxrZWVwZXIgb2YgZWFjaCB0ZWFtIiwKICAgICAgIHg9IkdvYWxrZWVwZXIgUmF0aW5nIiwKICAgICAgIHk9IkNvdW50cnkiKQpgYGAKIyMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IGRlZmVuc2UKCmBgYHtyfQojIEdhdGhlciBnb2Fsa2VlcGVyIGFuZCBkZWZlbnNlIGRhdGEgZnJvbSBtYXRjaGVzCmRlZl9ob21lIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGhvbWVfdGVhbSwgaG9tZV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUsIGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcgPSBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZSwgbWVhbl9kZWZlbnNlX3JhdGluZyA9IGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUpCgpkZWZfYXdheSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBhd2F5X3RlYW0sIGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLCBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBhd2F5X3RlYW0sIGdvYWxrZWVwZXJfcmF0aW5nID0gYXdheV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUsIG1lYW5fZGVmZW5zZV9yYXRpbmcgPSBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlKQoKZGVmX3JhdGluZyA8LSBkcm9wX25hKHJiaW5kKGRlZl9ob21lLCBkZWZfYXdheSkpCgojIEdldCBsYXRlc3QgY29tYmluZWQgcmF0aW5nIG9mIGVhY2ggdGVhbSBhbmQgc2hvdyB0b3AgMTAKbGF0ZXN0X2RlZl9yYXRpbmcgPC0KICBkZWZfcmF0aW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBtdXRhdGUodG90YWxfZGVmID0gZ29hbGtlZXBlcl9yYXRpbmcgKyBtZWFuX2RlZmVuc2VfcmF0aW5nKSAlPiUgCiAgZ3JvdXBfYnkodGVhbSkgJT4lIAogIG11dGF0ZShyb3dfbnVtYmVyID0gcm93X251bWJlcih0ZWFtKSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyPT0xKSAlPiUgCiAgYXJyYW5nZSgtdG90YWxfZGVmKSAlPiUgCiAgc2VsZWN0KC1yb3dfbnVtYmVyLCAtZGF0ZSkKCmdncGxvdChkYXRhID0gaGVhZChsYXRlc3RfZGVmX3JhdGluZywgMTApLCBtYXBwaW5nPWFlcyh4PXRvdGFsX2RlZiwgeT1yZW9yZGVyKHRlYW0sIHRvdGFsX2RlZiksIGxhYmVsPXRvdGFsX2RlZikpICsgCiAgZ2VvbV9jb2woZmlsbD0iIzg4QzBEMCIpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3QgZGVmZW5zZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBnb2Fsa2VlcGVyIGFuZCBtZWFuIGRlZmVuc2UgcmF0aW5ncyIsCiAgICAgICB4ID0gIlRvdGFsIERlZmVuc2UgUmF0aW5nIiwKICAgICAgIHkgPSAiVGVhbXMiKSAKYGBgCgojIyMgVGVhbXMgd2l0aCBzdHJvbmdlc3QgbWlkZmllbGQKCmBgYHtyfQptaWRfaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBob21lX3RlYW0sIG1pZGZpZWxkX3JhdGluZyA9IGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlKQoKbWlkX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSkgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCBtaWRmaWVsZF9yYXRpbmcgPSBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSkKCm1pZF9yYXRpbmcgPC0gZHJvcF9uYShyYmluZChtaWRfaG9tZSwgbWlkX2F3YXkpKQoKIyBHZXQgbGF0ZXN0IG1pZGZpZWxkIHJhdGluZyBvZiBlYWNoIHRlYW0gYW5kIHNob3cgdG9wIDEwCmxhdGVzdF9taWRfcmF0aW5nIDwtCiAgbWlkX3JhdGluZyAlPiUgCiAgYXJyYW5nZSh0ZWFtLCBkZXNjKGRhdGUpKSAlPiUgCiAgZ3JvdXBfYnkodGVhbSkgJT4lIAogIG11dGF0ZShyb3dfbnVtYmVyID0gcm93X251bWJlcih0ZWFtKSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyID09IDEpICU+JSAKICBhcnJhbmdlKC1taWRmaWVsZF9yYXRpbmcpICU+JSAKICBzZWxlY3QoLWRhdGUsIC1yb3dfbnVtYmVyKQoKZ2dwbG90KGRhdGEgPSBoZWFkKGxhdGVzdF9taWRfcmF0aW5nLCAxMCksIG1hcHBpbmc9YWVzKHg9bWlkZmllbGRfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgbWlkZmllbGRfcmF0aW5nKSwgbGFiZWw9bWlkZmllbGRfcmF0aW5nKSkgKyAKICBnZW9tX2NvbChmaWxsPSAiIzg4QzBEMCIpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3QgbWlkZmllbGQiLAogICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gdGhlIGF2ZXJhZ2UgcmF0aW5nIG9mIHRoZSA0IGhpZ2hlc3QgcmF0ZWQgbWlkZmllbGQgcGxheWVycyBvZiBlYWNoIHRlYW0iLAogICAgICAgeCA9ICJNaWRmaWVsZCBSYXRpbmciLAogICAgICAgeSA9ICJUZWFtcyIpCmBgYAoKIyMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IG9mZmVuc2UKCmBgYHtyfQpvZmZfaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9tZWFuX29mZmVuc2Vfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgb2ZmZW5zZV9yYXRpbmcgPSBob21lX3RlYW1fbWVhbl9vZmZlbnNlX3Njb3JlKQoKb2ZmX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fbWVhbl9vZmZlbnNlX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBhd2F5X3RlYW0sIG9mZmVuc2VfcmF0aW5nID0gYXdheV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSkKCm9mZl9yYXRpbmcgPC0gZHJvcF9uYShyYmluZChvZmZfaG9tZSwgb2ZmX2F3YXkpKQoKIyBHZXQgbGF0ZXN0IG9mZmVuc2UgcmF0aW5nIG9mIGVhY2ggdGVhbSBhbmQgc2hvdyB0b3AgMTAKbGF0ZXN0X29mZl9yYXRpbmcgPC0KICBvZmZfcmF0aW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBncm91cF9ieSh0ZWFtKSAlPiUgCiAgbXV0YXRlKHJvd19udW1iZXIgPSByb3dfbnVtYmVyKHRlYW0pKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIgPT0gMSkgJT4lIAogIGFycmFuZ2UoLW9mZmVuc2VfcmF0aW5nKSAlPiUgCiAgc2VsZWN0KC1kYXRlLCAtcm93X251bWJlcikKCmdncGxvdChkYXRhID0gaGVhZChsYXRlc3Rfb2ZmX3JhdGluZywgMTApLCBtYXBwaW5nPWFlcyh4PW9mZmVuc2VfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgb2ZmZW5zZV9yYXRpbmcpLCBsYWJlbD1vZmZlbnNlX3JhdGluZykpICsKICBnZW9tX2NvbChmaWxsPSIjODhDMEQwIikgKwogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGU9IlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3Qgb2ZmZW5zZSIsCiAgICAgIHN1YnRpdGxlPSJCYXNlZCBvbiB0aGUgYXZlcmFnZSByYXRpbmcgb2YgdGhlIDMgaGlnaGVzdCByYXRlZCBvZmZlbnNpdmUgcGxheWVycyBvZiBlYWNoIHRlYW0iLAogICAgICB4PSJPZmZlbnNlIFJhdGluZyIsCiAgICAgIHk9IlRlYW1zIikKYGBgCgojIyBJcyBpdCBiZXR0ZXIgdG8gcGxheSBhdCBob21lID8KCmBgYHtyfQpob21lX3RlYW1fYWR2YW50YWdlIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgZmlsdGVyKG5ldXRyYWxfbG9jYXRpb24gPT0gRkFMU0UpICU+JSAKICBjb3VudChob21lX3RlYW1fcmVzdWx0KSAlPiUgCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBsYWJlbF9wZXJjZW50KCkobi9zdW0obikpKQoKZ2dwbG90KGRhdGEgPSBob21lX3RlYW1fYWR2YW50YWdlLCBtYXBwaW5nPWFlcyh4PSIiLCB5PW4sIGZpbGw9aG9tZV90ZWFtX3Jlc3VsdCkpICsKICBnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvcj0id2hpdGUiKSArCiAgY29vcmRfcG9sYXIoInkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0VCQ0I4QiIsICIjQkY2MTZBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjQTNCRThDIikpICsKICB0aGVtZV92b2lkKCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIG1hdGNoIHJlc3VsdHMgb2YgaG9tZSB0ZWFtcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJFeGNsdWRpbmcgbWF0Y2hlcyBwbGF5ZWQgYXQgbmV1dHJhbCBsb2NhdGlvbnMiLAogICAgICAgZmlsbD0iUmVzdWx0IikKYGBgCgojIyBDb3JyZWxhdGlvbiBNYXRyaXgKCmBgYHtyfQojIHNlbGVjdCBudW1lcmljIGNvbHVtbnMgb25seQppbnB1dF9udW1lcmljX2RhdGEgPC0gaW5wdXRfZGF0YSAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lCiAgZHJvcF9uYSgpCgojIHJlbmFtZSB2YXJpYWJsZXMgZm9yIGVhc2llciBjb3JyZWxhdGlvbiBwbG90IHZpc3VhbGl6YXRpb24KaW5wdXRfbnVtZXJpY19kYXRhIDwtIGlucHV0X251bWVyaWNfZGF0YSAlPiUgcmVuYW1lKAogIHJhbmsxID0gaG9tZV90ZWFtX2ZpZmFfcmFuaywKICByYW5rMiA9IGF3YXlfdGVhbV9maWZhX3JhbmssCiAgdG90YWxfZmlmYV9wb2ludHMxID0gaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzLAogIHRvdGFsX2ZpZmFfcG9pbnRzMiA9IGF3YXlfdGVhbV90b3RhbF9maWZhX3BvaW50cywKICBzY29yZTEgPSBob21lX3RlYW1fc2NvcmUsCiAgc2NvcmUyID0gYXdheV90ZWFtX3Njb3JlLAogIGdrX3Njb3JlMSA9IGhvbWVfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLAogIGdrX3Njb3JlMiA9IGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLAogIGRmX3Njb3JlMSA9IGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUsCiAgZGZfc2NvcmUyID0gYXdheV90ZWFtX21lYW5fZGVmZW5zZV9zY29yZSwKICBhdHRfc2NvcmUxID0gaG9tZV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSwKICBhdHRfc2NvcmUyID0gYXdheV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSwKICBtZl9zY29yZTEgPSBob21lX3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSwKICBtZl9zY29yZTIgPSBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZQopCgojIGNyZWF0ZSBjb3JyZWxhdGlvbiBwbG90CmlucHV0X251bWVyaWNfZGF0YSAlPiUKICBjb3IoKSAlPiUKICBjb3JycGxvdCgKICAgIHR5cGUgPSAidXBwZXIiLAogICAgZGlhZyA9IEZBTFNFLAogICAgY29sPWNvbG9yUmFtcFBhbGV0dGUoYygiZmlyZWJyaWNrIiwibGlnaHR5ZWxsb3ciLCJncmVlbjQiKSkoMTAwKSwKICAgIG1ldGhvZCA9ICJzaGFkZSIsCiAgICBzaGFkZS5jb2wgPSBOQSwKICAgIHRsLmNvbCA9ICJibGFjayIsCiAgICB0bC5zcnQgPSA0NQogICkKYGBgCgojIDIuIERhdGEgUHJvY2Vzc2luZyAvIEZlYXR1cmUgRW5naW5lZXJpbmcKCiMjIENyZWF0ZSBuZXcgZmVhdHVyZXMKCmBgYHtyfQpvdXRwdXRfZGF0YSA8LSBpbnB1dF9kYXRhCgpvdXRwdXRfZGF0YSRyYW5rX2RpZmYgPC0gb3V0cHV0X2RhdGEkaG9tZV90ZWFtX2ZpZmFfcmFuayAtIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9maWZhX3JhbmsKCm91dHB1dF9kYXRhJGF2ZXJhZ2VfcmFuayA8LSAob3V0cHV0X2RhdGEkaG9tZV90ZWFtX2ZpZmFfcmFuayArIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9maWZhX3JhbmspLzIKCm91dHB1dF9kYXRhJHBvaW50X2RpZmYgPC0gb3V0cHV0X2RhdGEkaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzIC0gb3V0cHV0X2RhdGEkYXdheV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzCgpvdXRwdXRfZGF0YSRzY29yZV9kaWZmIDwtIG91dHB1dF9kYXRhJGhvbWVfdGVhbV9zY29yZSAtIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9zY29yZQoKb3V0cHV0X2RhdGEkd2luIDwtIG91dHB1dF9kYXRhJHNjb3JlX2RpZmYgPiAwCgpvdXRwdXRfZGF0YSRzdGFrZSA8LSBvdXRwdXRfZGF0YSR0b3VybmFtZW50ICE9ICdGcmllbmRseScKYGBgCgojIyBNb2RlbAoKYGBge3J9CiMgY3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0IHNldApzYW1wbGUgPC0KICBzYW1wbGUoYyhUUlVFLCBGQUxTRSksCiAgICAgICAgIG5yb3cob3V0cHV0X2RhdGEpLAogICAgICAgICByZXBsYWNlID0gVFJVRSwKICAgICAgICAgcHJvYiA9IGMoMC43LCAwLjMpKQp0cmFpbiA8LSBvdXRwdXRfZGF0YVtzYW1wbGUsXQp0ZXN0IDwtIG91dHB1dF9kYXRhWyFzYW1wbGUsXQoKIyBmaXQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApsb2dyZWcgPC0KICBnbG0od2luIH4gYXZlcmFnZV9yYW5rICsgcmFua19kaWZmICsgcG9pbnRfZGlmZiwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IHRyYWluKQpzdW1tYXJ5KGxvZ3JlZykKCiMgY2FsYyBwcm9iYWJpbGl0eSBvZiB3aW4gZm9yIGVhY2ggdGVhbSBpbiB0ZXN0IGRhdGFzZXQKcHJlZGljdGVkIDwtIHByZWRpY3QobG9ncmVnLCB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKCgojIGNvbnZlcnQgd2lucyB0byAxIGFuZCAwCnRlc3Qkd2luIDwtIGlmZWxzZSh0ZXN0JHdpbiA9PSBUUlVFLCAxLCAwKQoKIyBmaW5kIG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5IHRvIHVzZSB0byBtYXhpbWl6ZSBhY2N1cmFjeQpvcHRpbWFsIDwtIG9wdGltYWxDdXRvZmYodGVzdCR3aW4sIHByZWRpY3RlZClbMV0Kb3B0aW1hbAoKIyBjb25mdXNpb24gbWF0cml4CmNvbmZ1c2lvbk1hdHJpeCh0ZXN0JHdpbiwgcHJlZGljdGVkKQoKIyBjYWxjdWxhdGUgbWlzcyBjbGFzc2lmaWNhdGlvbiBlcnJvciByYXRlCm1pc0NsYXNzRXJyb3IodGVzdCR3aW4sIHByZWRpY3RlZCwgdGhyZXNob2xkID0gb3B0aW1hbCkKCiMgUk9DCnBsb3RST0ModGVzdCR3aW4sIHByZWRpY3RlZCkKCmBgYAoKIyMgVGVzdAoKYGBge3J9CndjX3RlYW1zIDwtIGxpc3QoJ1FhdGFyJywgJ0VjdWFkb3InLCAnU2VuZWdhbCcsICdOZXRoZXJsYW5kcycsICdFbmdsYW5kJywgJ0lyYW4nLCAnVVNBJywKICAgICAgICAgICAgICAgICAgJ1dhbGVzJywgJ0FyZ2VudGluYScsICdTYXVkaSBBcmFiaWEnLCAnTWV4aWNvJywgJ1BvbGFuZCcsICdGcmFuY2UnLCAKICAgICAgICAgICAgICAgICAgJ0F1c3RyYWxpYScsICdEZW5tYXJrJywgJ1R1bmlzaWEnLCAnU3BhaW4nLCAnQ29zdGEgUmljYScsICdHZXJtYW55JywgCiAgICAgICAgICAgICAgICAgICdKYXBhbicsICdCZWxnaXVtJywgJ0NhbmFkYScsICdNb3JvY2NvJywgJ0Nyb2F0aWEnLCAnQnJhemlsJywgJ1NlcmJpYScsIAogICAgICAgICAgICAgICAgICAnU3dpdHplcmxhbmQnLCAnQ2FtZXJvb24nLCAnUG9ydHVnYWwnLCAnR2hhbmEnLCAnVXJ1Z3VheScsICdTb3V0aCBLb3JlYScpCgp3Y19yYW5raW5nc19ob21lIDwtIG91dHB1dF9kYXRhICU+JSAKICBmaWx0ZXIoZGF0ZT4iMjAxMy0wMS0wMSIpICU+JSAKICBzZWxlY3QoaG9tZV90ZWFtLCBob21lX3RlYW1fZmlmYV9yYW5rLCBob21lX3RlYW1fdG90YWxfZmlmYV9wb2ludHMpICU+JSAKICBmaWx0ZXIoaG9tZV90ZWFtICVpbiUgd2NfdGVhbXMpCiAgCndjX3JhbmtpbmdzX2F3YXkgPC0gb3V0cHV0X2RhdGEgJT4lIAogIGZpbHRlcihkYXRlPiIyMDEzLTAxLTAxIikgJT4lIAogIHNlbGVjdChhd2F5X3RlYW0sIGF3YXlfdGVhbV9maWZhX3JhbmssIGF3YXlfdGVhbV90b3RhbF9maWZhX3BvaW50cykgJT4lIAogIGZpbHRlcihhd2F5X3RlYW0gJWluJSB3Y190ZWFtcykKYGBgCgpgYGB7cn0KIyBwcmVwYXJlIGxpc3RzCnNpbXVsYXRpb25fd2lubmVycyA8LSBsaXN0KCkKc2ltdWxhdGlvbl9yZXN1bHRzX3dpbm5lcnMgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19yb3VuZDE2IDwtIGxpc3QoKQpzaW11bGF0aW9uX2RmX3JvdW5kMTYgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19xdWFydGVyZmluYSA8LSBsaXN0KCkKc2ltdWxhdGlvbl9kZl9xdWFydGVyZmluYWwgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19zZW1pZmluYSA8LSBsaXN0KCkKc2ltdWxhdGlvbl9kZl9zZW1pZmluYWwgPC0gbGlzdCgpCgojIHNpbXVsYXRpb25zCm4gPSAxMDAwCgojIHNlbGVjdCB3aG8gd2lsbCBjb21lIG91dCBvZiB0aGUgZ3JvdXAgc3RhZ2VzCmNhbmRpZGF0ZXMgPC0KICBsaXN0KAogICAgJ1NlbmVnYWwnLAogICAgJ05ldGhlcmxhbmRzJywKICAgICdFbmdsYW5kJywKICAgICdVU0EnLAogICAgJ0FyZ2VudGluYScsCiAgICAnUG9sYW5kJywKICAgICdGcmFuY2UnLAogICAgJ0Rlbm1hcmsnLAogICAgJ1NwYWluJywKICAgICdHZXJtYW55JywKICAgICdCZWxnaXVtJywKICAgICdDcm9hdGlhJywKICAgICdCcmF6aWwnLAogICAgJ1NlcmJpYScsCiAgICAnUG9ydHVnYWwnLAogICAgJ1VydWd1YXknCiAgKQpmaW5hbHMgPSBsaXN0KCdyb3VuZF9vZl8xNicsICdxdWFydGVyZmluYWwnLCAnc2VtaWZpbmFsJywgJ2ZpbmFsJykKCiMgc2ltdWxhdGUKCmZvciAoZiBpbiBmaW5hbHMpewogIGl0ZXJhdGlvbnMgPC0gbGVuZ3RoKGNhbmRpZGF0ZXMpLzIKICB3aW5uZXJzID0gbGlzdCgpCiAgcHJvYiA9IGxpc3QoKQogIAogIGZvciAoaSBpbiByYW5nZShpdGVyYXRpb25zKSl7CiAgICBob21lIDwtIGNhbmRpZGF0ZXNbaSoyXQogICAgYXdheSA8LSBjYW5kaWRhdGVzW2kqMisxXQogICAgCiAgICByb3cgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IDAsIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVzdCkpKSkKICAgIGNvbG5hbWVzKHJvdykgPC0gY29sbmFtZXModGVzdCkKICAgIAogICAgaG9tZV9yYW5rIDwtIHdjX3JhbmtpbmdzX2hvbWUgJT4lIGZpbHRlcgogICAgCiAgICAKICAgIAogICAgCiAgfQp9CiAgCgoKCgoKCgoKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCg==